function Buffer(){

}
(function (cp){
    cp.length = 0;
    cp._joined = '';
    cp._chunks = null;

    var CHUNK_SIZE = 4096;
    cp.getString = function (){
        return this._joined;
    };

    cp.substring = function (start, end){
        return this._joined.slice(start, end);
    };

    cp._getString = function (){
        this._joined = this._chunks.join('');
        this._applyJoined(true);
        return this._joined;
    };

    cp.setString = function (string){
        this._joined = string;
        this.length = string.length;
        this._applyJoined(true);
    };

    cp._substring = function (start, end){
        if (start >= this.length) {
            return '';
        }
        var chunks = this._chunks;
        var offset = 0;
        var startIndex = -1;
        var startChunk = '';
        while (offset <= start) {
            startChunk = chunks[++ startIndex];
            offset += startChunk.length;
        }
        var startOffset = start - (offset - startChunk.length);
        var endIndex = startIndex;
        var endChunk = startChunk;
        while (offset < end) {
            endChunk = chunks[++ endIndex];
            offset += endChunk.length;
        }
        var endOffset = end - offset; // it should be negative.
        if (startIndex === 0 &&
            endIndex + 1 === chunks.length) {
            if (endOffset) {
                return this.getString().slice(startOffset, endOffset);
            } else {
                return this.getString().slice(startOffset);
            }
        }
        if (startIndex === endIndex) {
            return startChunk.slice(startOffset, endOffset);
        }
        var joined = chunks.slice(startIndex, endIndex + 1).join('');
        chunks.splice(startIndex, endIndex + 1, joined);
        return joined.slice(startOffset, endOffset);
    };

    var Array$splice = Array.prototype.splice;
    cp.spliceString = function (start, end, string){
        if (start === 0 &&
            end === this.length) {
            this.setString(string);
            return;
        }
        var chunks = this._chunks || [this._joined];
        if (start >= this.length) {
            if (string) {
                var lastIndex = chunks.length - 1;
                var lastChunk = chunks[lastIndex];
                if (lastChunk.length < CHUNK_SIZE &&
                    string.length < CHUNK_SIZE) {
                    chunks[lastIndex] = lastChunk + string;
                } else {
                    chunks.push(string);
                }
            }
        } else {
            var offset = 0;
            var startIndex = -1;
            var startChunk = '';
            while (offset <= start) {
                startChunk = chunks[++ startIndex];
                offset += startChunk.length;
            }
            var startLength = start - (offset - startChunk.length);
            var endIndex = startIndex;
            var endChunk = startChunk;
            while (offset < end) {
                endChunk = chunks[++ endIndex];
                offset += endChunk.length;
            }
            var endLength = offset - end;

            var tempChunks = [
                chunks[startIndex - 1],
                startChunk.slice(0, startLength),
                string,
                endLength ? endChunk.slice(-endLength) : '',
                chunks[endIndex + 1]
            ];
            startIndex = Math.max(0, startIndex - 1);
            var paramArray = [startIndex, endIndex - startIndex + 1];
            var buff = [];
            var buffLength = 0;
            for (var i=0; i<tempChunks.length; i++) {
                var tempChunk = tempChunks[i];
                if (tempChunk) {
                    buff.push(tempChunk);
                    buffLength += tempChunk.length;
                    if (buffLength >= CHUNK_SIZE) {
                        tempChunk = buff.join('');
                        paramArray.push(tempChunk);
                        buff.length = 0;
                        buffLength = 0;
                    }
                }
            }
            if (buff.length) {
                paramArray.push(buff.join(''));
            }
            Array$splice.apply(chunks, paramArray);
        }
        this.length += start - end + string.length;
        if (chunks.length === 1) {
            this.setString(chunks[0]);
        } else {
            if (this._chunks === null) {
                this._chunks = chunks;
                this._applyJoined(false);
            }
        }
    };

    cp._applyJoined = function (joined){
        if (joined) {
            this._chunks = null;
            delete this.getString;
            delete this.substring;
        } else {
            this._joined = null;
            this.getString = this._getString;
            this.substring = this._substring;
        }
    };
})(Buffer.prototype);
